package cn.caplike.demo.spring.security.csrf.filter;

import cn.caplike.data.redis.service.spring.boot.starter.RedisKey;
import cn.caplike.data.redis.service.spring.boot.starter.RedisService;
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 放行登陆端口, 登陆成功之后返回 CSRF-TOKEN. 后续访问的时候前端均带上
 * <br>
 * 晚于 CsrfFilter 执行
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-04-26 13:40
 */
@Slf4j
public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final RedisService redisService;

    private final AuthenticationManager authenticationManager;

    public SimpleAuthenticationFilter(AuthenticationManager authenticationManager, RedisService redisService) {
        this.authenticationManager = authenticationManager;
        this.redisService = redisService;
    }

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("simple authentication filter: attemptAuthentication ...");

        Map<String, String> map = JSON.parseObject(request.getInputStream(), StandardCharsets.UTF_8, Map.class);

        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
                MapUtils.getString(map, "username"),
                MapUtils.getString(map, "password")
        ));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) {
        SecurityContextHolder.getContext().setAuthentication(authResult);

        UserDetails userDetails = (UserDetails) authResult.getPrincipal();

        // 登陆成功把 csrf-token 返回给前端
        response.setHeader("csrf-token", redisService.getValue(RedisKey.builder().prefix("user").suffix(userDetails.getUsername()).build(), String.class));
    }
}
